/*
 *  Copyright (C) 2010 Zubanov Dmitry
 *
 *  This program is free software: you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation, either version 3 of the License, or
 *  (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */


/**
 *
 * @author Zubanov Dmitry aka javadimon, zubanov@gmail.com
 * Omsk, Russia, created 29.05.2010
 * (C) Copyright by Zubanov Dmitry
 */

package org.jssdb.core.josql;

import java.util.ArrayList;
import java.util.Map;
import java.util.logging.Level;
import java.util.logging.Logger;
import net.sf.jsqlparser.expression.AllComparisonExpression;
import net.sf.jsqlparser.expression.AnyComparisonExpression;
import net.sf.jsqlparser.expression.CaseExpression;
import net.sf.jsqlparser.expression.DateValue;
import net.sf.jsqlparser.expression.DoubleValue;
import net.sf.jsqlparser.expression.Expression;
import net.sf.jsqlparser.expression.ExpressionVisitor;
import net.sf.jsqlparser.expression.Function;
import net.sf.jsqlparser.expression.InverseExpression;
import net.sf.jsqlparser.expression.JdbcParameter;
import net.sf.jsqlparser.expression.LongValue;
import net.sf.jsqlparser.expression.NullValue;
import net.sf.jsqlparser.expression.Parenthesis;
import net.sf.jsqlparser.expression.StringValue;
import net.sf.jsqlparser.expression.TimeValue;
import net.sf.jsqlparser.expression.TimestampValue;
import net.sf.jsqlparser.expression.WhenClause;
import net.sf.jsqlparser.expression.operators.arithmetic.Addition;
import net.sf.jsqlparser.expression.operators.arithmetic.Division;
import net.sf.jsqlparser.expression.operators.arithmetic.Multiplication;
import net.sf.jsqlparser.expression.operators.arithmetic.Subtraction;
import net.sf.jsqlparser.expression.operators.conditional.AndExpression;
import net.sf.jsqlparser.expression.operators.conditional.OrExpression;
import net.sf.jsqlparser.expression.operators.relational.Between;
import net.sf.jsqlparser.expression.operators.relational.EqualsTo;
import net.sf.jsqlparser.expression.operators.relational.ExistsExpression;
import net.sf.jsqlparser.expression.operators.relational.GreaterThan;
import net.sf.jsqlparser.expression.operators.relational.GreaterThanEquals;
import net.sf.jsqlparser.expression.operators.relational.InExpression;
import net.sf.jsqlparser.expression.operators.relational.IsNullExpression;
import net.sf.jsqlparser.expression.operators.relational.LikeExpression;
import net.sf.jsqlparser.expression.operators.relational.MinorThan;
import net.sf.jsqlparser.expression.operators.relational.MinorThanEquals;
import net.sf.jsqlparser.expression.operators.relational.NotEqualsTo;
import net.sf.jsqlparser.schema.Column;
import net.sf.jsqlparser.statement.select.SubSelect;


public class WherePrepare implements ExpressionVisitor {

    private static final Logger logger = Logger.getLogger(WherePrepare.class.getName());
    private ArrayList<WhereTreeNode> arrayTree = new ArrayList();
    private int parent = 0;

    public void prepare(ArrayList<Map> resultArray, Map selectMap) {
        // TODO
        Object obj = selectMap.get("where");
        if (obj instanceof Parenthesis) {
            Parenthesis p = (Parenthesis) obj;
            Expression e = p.getExpression();
            e.accept(this);
        }

        if (obj instanceof AndExpression) {
            AndExpression ae = (AndExpression) obj;
            ae.accept(this);
        }

        if (obj instanceof LikeExpression) {
            LikeExpression le = (LikeExpression) obj;
            le.accept(this);
        }

        if (obj instanceof OrExpression) {
            OrExpression oe = (OrExpression) obj;
            oe.accept(this);
        }

        if (obj instanceof EqualsTo) {
            EqualsTo et = (EqualsTo) obj;
            et.accept(this);
        }

        if (obj instanceof GreaterThanEquals) {
            GreaterThanEquals gte = (GreaterThanEquals) obj;
            gte.accept(this);
        }

        if (obj instanceof GreaterThan) {
            GreaterThan gt = (GreaterThan) obj;
            gt.accept(this);
        }

        if (obj instanceof MinorThanEquals) {
            MinorThanEquals mte = (MinorThanEquals) obj;
            mte.accept(this);
        }

        if (obj instanceof MinorThan) {
            MinorThan mt = (MinorThan) obj;
            mt.accept(this);
        }

        if (obj instanceof NotEqualsTo) {
            NotEqualsTo net = (NotEqualsTo) obj;
            net.accept(this);
        }

        if (obj instanceof IsNullExpression) {
            IsNullExpression ine = (IsNullExpression) obj;
            ine.accept(this);
        }

        logger.log(Level.INFO, "{0}", obj.getClass());
    }

    public void visit(NullValue nullValue) {
        throw new UnsupportedOperationException("Not supported yet.");
    }

    public void visit(Function function) {
        throw new UnsupportedOperationException("Not supported yet.");
    }

    public void visit(InverseExpression inverseExpression) {
        throw new UnsupportedOperationException("Not supported yet.");
    }

    public void visit(JdbcParameter jdbcParameter) {
        throw new UnsupportedOperationException("Not supported yet.");
    }

    public void visit(DoubleValue doubleValue) {
        ((WhereTreeNode) arrayTree.get(arrayTree.size() - 1)).setValue(doubleValue.getValue());
    }

    public void visit(LongValue longValue) {
        ((WhereTreeNode) arrayTree.get(arrayTree.size() - 1)).setValue(longValue.getValue());
    }

    public void visit(DateValue dateValue) {
        ((WhereTreeNode) arrayTree.get(arrayTree.size() - 1)).setValue(dateValue.getValue());
    }

    public void visit(TimeValue timeValue) {
        ((WhereTreeNode) arrayTree.get(arrayTree.size() - 1)).setValue(timeValue.getValue());
    }

    public void visit(TimestampValue timestampValue) {
        ((WhereTreeNode) arrayTree.get(arrayTree.size() - 1)).setValue(timestampValue.getValue());
    }

    public void visit(Parenthesis parenthesis) {
        Expression e = parenthesis.getExpression();
        e.accept(this);
    }

    public void visit(StringValue stringValue) {
        ((WhereTreeNode) arrayTree.get(arrayTree.size() - 1)).setValue(stringValue.getValue());
    }

    public void visit(Addition addition) {
        addRow(addition.getStringExpression(), "addition", false, true, null);

        Expression eLeft = addition.getLeftExpression();
        addRow(eLeft, "addition", true, false, null);
        eLeft.accept(this);

        Expression eRight = addition.getRightExpression();
        addRow(eRight, "addition", false, false, null);
        eRight.accept(this);
    }

    public void visit(Division division) {
        //
    }

    public void visit(Multiplication multiplication) {
        //
    }

    public void visit(Subtraction subtraction) {
        //
    }

    public void visit(AndExpression andExpression) {
        addRow(andExpression.getStringExpression(), "andExpression", false, true, null);

        Expression eLeft = andExpression.getLeftExpression();
        eLeft.accept(this);

        Expression eRight = andExpression.getRightExpression();
        eRight.accept(this);
    }

    public void visit(OrExpression orExpression) {
        addRow(orExpression.getStringExpression(), "orExpression", false, true, null);

        Expression eLeft = orExpression.getLeftExpression();
        eLeft.accept(this);

        Expression eRight = orExpression.getRightExpression();
        eRight.accept(this);
    }

    public void visit(Between between) {
        throw new UnsupportedOperationException("Not supported yet.");
    }

    public void visit(EqualsTo equalsTo) {
        Expression eLeft = equalsTo.getLeftExpression();
        logger.log(Level.INFO, "equalsTo.getLeftExpression() = {0}", equalsTo.getLeftExpression().getClass());
        if (isGeneric(equalsTo.getLeftExpression())) {
            addRow(equalsTo.getLeftExpression(), "equalsTo", true, false, null);
        }
        eLeft.accept(this);

        Expression eRight = equalsTo.getRightExpression();
        logger.log(Level.INFO, "equalsTo.getRightExpression() = {0}", equalsTo.getRightExpression().getClass());
        if (isGeneric(equalsTo.getRightExpression())) {
            addRow(equalsTo.getRightExpression(), "equalsTo", false, false, null);
        }
        eRight.accept(this);
    }

    public void visit(GreaterThan greaterThan) {
        Expression eLeft = greaterThan.getLeftExpression();
        addRow(greaterThan.getLeftExpression().toString(), "greaterThan", true, false, null);
        eLeft.accept(this);

        Expression eRight = greaterThan.getRightExpression();
        addRow(greaterThan.getRightExpression().toString(), "greaterThan", false, false, null);
        eRight.accept(this);
    }

    public void visit(GreaterThanEquals greaterThanEquals) {
        Expression eLeft = greaterThanEquals.getLeftExpression();
        addRow(greaterThanEquals.getLeftExpression().toString(), "greaterThanEquals", true, false, null);
        eLeft.accept(this);

        Expression eRight = greaterThanEquals.getRightExpression();
        addRow(greaterThanEquals.getRightExpression().toString(), "greaterThanEquals", false, false, null);
        eRight.accept(this);
    }

    public void visit(InExpression inExpression) {
        throw new UnsupportedOperationException("Not supported yet.");
    }

    public void visit(IsNullExpression isNullExpression) {
        Expression eLeft = isNullExpression.getLeftExpression();
        addRow(isNullExpression.getLeftExpression().toString(), "isNullExpression", true, false, isNullExpression.isNot());
        eLeft.accept(this);

        addRow("" + isNullExpression.isNot(), "isNullExpression", false, false, null);
    }

    public void visit(LikeExpression likeExpression) {
        Expression eLeft = likeExpression.getLeftExpression();
        addRow(likeExpression.getLeftExpression().toString(), "likeExpression", true, false, likeExpression.isNot());
        eLeft.accept(this);

        Expression eRight = likeExpression.getRightExpression();
        addRow(likeExpression.getRightExpression().toString(), "likeExpression", false, false, likeExpression.isNot());
        eRight.accept(this);
    }

    public void visit(MinorThan minorThan) {
        Expression eLeft = minorThan.getLeftExpression();
        addRow(minorThan.getLeftExpression().toString(), "minorThan", true, false, null);
        eLeft.accept(this);

        Expression eRight = minorThan.getRightExpression();
        addRow(minorThan.getRightExpression().toString(), "minorThan", false, false, null);
        eRight.accept(this);
    }

    public void visit(MinorThanEquals minorThanEquals) {
        Expression eLeft = minorThanEquals.getLeftExpression();
        addRow(minorThanEquals.getLeftExpression().toString(), "minorThanEquals", true, false, null);
        eLeft.accept(this);

        Expression eRight = minorThanEquals.getRightExpression();
        addRow(minorThanEquals.getRightExpression().toString(), "minorThanEquals", false, false, null);
        eRight.accept(this);
    }

    public void visit(NotEqualsTo notEqualsTo) {
        Expression eLeft = notEqualsTo.getLeftExpression();
        addRow(notEqualsTo.getLeftExpression().toString(), "notEqualsTo", true, false, null);
        eLeft.accept(this);

        Expression eRight = notEqualsTo.getRightExpression();
        addRow(notEqualsTo.getRightExpression().toString(), "notEqualsTo", false, false, null);
        eRight.accept(this);
    }

    public void visit(Column tableColumn) {
        //logger.log(Level.INFO, "tableColumn {0}", tableColumn.getColumnName());
    }

    public void visit(SubSelect subSelect) {
        throw new UnsupportedOperationException("Not supported yet.");
    }

    public void visit(CaseExpression caseExpression) {
        throw new UnsupportedOperationException("Not supported yet.");
    }

    public void visit(WhenClause whenClause) {
        throw new UnsupportedOperationException("Not supported yet.");
    }

    public void visit(ExistsExpression existsExpression) {
        throw new UnsupportedOperationException("Not supported yet.");
    }

    public void visit(AllComparisonExpression allComparisonExpression) {
        throw new UnsupportedOperationException("Not supported yet.");
    }

    public void visit(AnyComparisonExpression anyComparisonExpression) {
        throw new UnsupportedOperationException("Not supported yet.");
    }

    private void addRow(Object val, String expression, boolean isLeftPart,
            boolean isNode, Object extObject) {
        WhereTreeNode whrereTreeNode = new WhereTreeNode(val, expression,
                isLeftPart, parent, isNode, extObject);
        arrayTree.add(whrereTreeNode);
        if (isNode) {
            parent = arrayTree.size() - 1;
        }
    }

    public ArrayList<WhereTreeNode> getArrayTree() {
        return arrayTree;
    }

    private boolean isGeneric(Object obj) {
        boolean result = false;
        if (obj instanceof LongValue) {
            result = true;
        }

        if (obj instanceof DoubleValue) {
            result = true;
        }

        if (obj instanceof StringValue) {
            result = true;
        }

        if (obj instanceof DoubleValue) {
            result = true;
        }

        if (obj instanceof DateValue) {
            result = true;
        }

        if (obj instanceof TimeValue) {
            result = true;
        }

        if (obj instanceof TimestampValue) {
            result = true;
        }

        if (obj instanceof Column){
            result = true;
        }

        return result;
    }
}
